use std::{collections::{HashMap, HashSet}, path::PathBuf};

use egui::Pos2;
use egui_snarl::Snarl;
use node::Node;
use node_tree::SnarlGenTree;
use serde::{Deserialize, Serialize};
use value::Value;
use viewer::Viewer;

use crate::data::GraphData;
pub mod value;
pub mod node;
mod viewer;
pub(crate) mod node_tree;

#[derive(Serialize, Deserialize)]
pub struct Graph {
    pub snarl: Snarl<Node>,
    pub graph_data: Option<GraphData>,
    pub custom_datas: HashMap<String, Value>,
    pub constants: HashMap<String, Value>,
    pub include_graphs: HashSet<String>,
    #[serde(skip)]
    pub viewer: Viewer,
    #[serde(skip)]
    pub style: egui_snarl::ui::SnarlStyle,
}

impl Clone for Graph {
    fn clone(&self) -> Self {
        Self { snarl: self.snarl.clone(), graph_data: self.graph_data.clone(), custom_datas: self.custom_datas.clone(), constants: self.constants.clone(), include_graphs: self.include_graphs.clone(), ..Default::default() }
    }
}

impl Graph {
    pub fn new() -> Self {
        let mut snarl = Snarl::default();
        snarl.insert_node(Pos2::new(0f32, 0f32), Node::new_root());
        Self {
            snarl: snarl,
            style: egui_snarl::ui::SnarlStyle::default(),
            viewer: Viewer::new(),
            graph_data: None,
            custom_datas: HashMap::new(),
            constants: HashMap::new(),
            include_graphs: HashSet::new(),
        }
    }

    pub fn init(&mut self, workdir: String) {
        self.viewer.constants = self.constants.clone();
        self.viewer.custom_datas = self.custom_datas.clone();
        self.reload_all_include_graphs(workdir);
    }

    pub fn clear_dirty(&mut self) {
        self.viewer.clear_dirty();
    }

    pub fn add_custom_data(&mut self, name: String, data: Value) {
        self.custom_datas.insert(name, data);
        self.viewer.custom_datas = self.custom_datas.clone();
    }

    pub fn remove_custom_data(&mut self, name: &str) {
        self.custom_datas.remove(name);
        self.viewer.custom_datas = self.custom_datas.clone();
    }

    pub fn add_constant(&mut self, name: String, data: Value) {
        self.constants.insert(name, data);
        self.viewer.constants = self.constants.clone();
    }

    pub fn remove_constant(&mut self, name: &str) {
        self.constants.remove(name);
        self.viewer.constants = self.constants.clone();
    }

    pub fn reload_all_include_graphs(&mut self, workdir: String) {
        let mut workdir: PathBuf = PathBuf::from(workdir);
        let mut result:HashMap<String, Graph> = HashMap::new();
        for path in self.include_graphs.iter() {
            workdir.push(path);
            if let Ok(graph) = Graph::load_from_path(&workdir) {
                result.insert(path.clone(), graph);
            }
            workdir.pop();
        }
        self.viewer.include_graphs = result;
    }

    pub fn add_include_graph(&mut self, name: String, workdir: String) {
        if self.include_graphs.insert(name) {
            self.reload_all_include_graphs(workdir);
        }
    }

    pub fn remove_include_graph(&mut self, name: &str, workdir: String) {
        if self.include_graphs.remove(name) {
            self.reload_all_include_graphs(workdir);
        }
    }
}

impl Graph {
    pub fn load_from_path<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
        let bytes = std::fs::read(path)?;
        let graph = bincode::deserialize(&bytes)?;
        Ok(graph)
    }
}

impl Default for Graph {
    fn default() -> Self {
        Self::new()
    }
}

impl Graph {
    pub fn show(&mut self, ui: &mut egui::Ui) {
        self.viewer.ui_id = Some(ui.id());
        if self.viewer.is_dirty() || self.viewer.current_tree.is_none() {
            self.viewer.current_tree = Some(self.snarl.gen_node_tree());
        }
        self.snarl.show(&mut self.viewer, &self.style, "snarl_main", ui);
    }

    pub fn set_graph_data(&mut self, graph_data: GraphData) {
        self.graph_data = Some(graph_data.clone());
        self.viewer.set_graph_data(graph_data);
    }

    // pub fn set_custom_datas(&mut self, custom_datas: HashMap<String, Value>) {
    //     self.custom_datas = custom_datas;
    //     self.viewer.custom_datas = self.custom_datas.clone();
    // }

    // pub fn set_constants(&mut self, constants: HashMap<String, Value>) {
    //     self.constants = constants;
    //     self.viewer.constants = self.constants.clone();
    // }

    // pub fn set_include_graphs(&mut self, include_graphs: HashSet<String>, workdir: String) {
    //     self.include_graphs = include_graphs;
    //     self.reload_all_include_graphs(workdir);
    // }
}
